package com.m4f.cityclient.oauth.consumer;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.util.SerializationUtils;
import org.springframework.util.Assert;

import com.google.api.client.auth.oauth2.AccessProtectedResource;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonParser;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.appengine.api.datastore.Blob;
import com.m4f.cityclient.api.models.UserCity;
import com.m4f.cityclient.api.service.ifc.M4FUserApiService;
import com.m4f.cityclient.common.model.M4FOAuthUser;
import com.m4f.cityclient.common.service.ifc.M4FOAuthUserService;
import com.m4f.gaeweb.web.utils.StackTraceUtil;


public class M4FAuthenticationProvider implements AuthenticationProvider, InitializingBean {
	private static final Logger LOGGER = Logger.getLogger(DataStoreOAuth2ClientTokenServices.class.getName());
	
	private UserDetailsService userDetailsService;
	private String m4fscUserInfoUrl; // URL to get the user information
	
	private M4FOAuthUserService oauthUserService;
	
	
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(this.userDetailsService, "The userDetailsService must be set");
	}

	@Override
	public Authentication authenticate(final Authentication authentication)
			throws AuthenticationException {
		if (!supports(authentication.getClass())) {
			return null;
		}
		
		M4FOAuthUser user = null;
		M4FOAuth2AuthenticationToken response = (M4FOAuth2AuthenticationToken) authentication;
		HttpTransport transport = new NetHttpTransport();
        AccessProtectedResource.usingQueryParameter(transport, response.getAccessToken().getValue());
        HttpRequest re = transport.buildGetRequest();
        re.url = new GenericUrl(getM4fscUserInfoUrl()/*"http://192.168.1.14:8080/app/api/user/info"*/);
           
        try {
        	DataInputStream iStream = new DataInputStream (re.execute().getContent());
        	StringBuffer sb = new StringBuffer();
        	byte[] bytes = new byte[1024];
        	while(iStream.read(bytes) > 0) {
        		sb.append(new String(bytes));
        		bytes = new byte[1024];
        	}
        	JsonFactory jsonFactory = new JacksonFactory();
			JsonParser parser = jsonFactory.createJsonParser(sb.toString());
			parser.nextToken();
			
			UserCity userCity = parser.parseAndClose(UserCity.class, null);			
			user = this.oauthUserService.createUser(userCity);
			
			user.getOauthAccessTokens().put(response.getServiceProviderId(), new Blob(SerializationUtils.serialize(response.getAccessToken())));
		} catch(IOException e) { // TODO Implement other exception managment
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
		/*} catch(SAXException e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));
		} catch(ParserConfigurationException e) {
			LOGGER.log(Level.SEVERE, StackTraceUtil.getStackTrace(e));*/
		}
    		
		// TODO Improve this part!!
		M4FOAuthUser userDetails = null;
		try {
			userDetails = (M4FOAuthUser) userDetailsService.loadUserByUsername(user.getEmail());
		} catch(AuthenticationException e) { // This could be the first time that the user attemp to login. So its time to save the user.
			try {
				this.oauthUserService.save(user);
				userDetails = (M4FOAuthUser) userDetailsService.loadUserByUsername(user.getEmail());
			} catch(Exception ex) {
				throw e;
			}
			
		}
		/*java.util.Collection<GrantedAuthority> authorities = 
			new java.util.ArrayList<GrantedAuthority>();
		authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
		userDetails.setAuthorities(authorities);*/
	
		return createSuccessfulAuthentication(userDetails, response);
    		
	}		

	protected Authentication createSuccessfulAuthentication(UserDetails userDetails, M4FOAuth2AuthenticationToken auth) {
		return new M4FOAuth2AuthenticationToken(userDetails, userDetails.getAuthorities(), auth.getAccessToken(), auth.getProviderAccountId(), auth.getServiceProviderId());
	}

	public UserDetailsService getUserDetailsService() {
		return userDetailsService;
	}

	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}

	@Override
	public boolean supports(Class<? extends Object> authentication) {
		return M4FOAuth2AuthenticationToken.class.isAssignableFrom(authentication);
	}
	
	public String getM4fscUserInfoUrl() {
		return m4fscUserInfoUrl;
	}

	public void setM4fscUserInfoUrl(String m4fscUserInfoUrl) {
		this.m4fscUserInfoUrl = m4fscUserInfoUrl;
	}

	public M4FOAuthUserService getOauthUserService() {
		return oauthUserService;
	}

	public void setOauthUserService(M4FOAuthUserService oauthUserService) {
		this.oauthUserService = oauthUserService;
	}
}
